package com.googlecode.wmlynar.xmlbeansbinding.maven;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.artifact.InvalidDependencyVersionException;
import org.apache.maven.project.artifact.MavenMetadataSource;

import com.googlecode.wmlynar.gwtbeans.gen.BindingFacoryGenerator;
import com.googlecode.wmlynar.gwtbeans.gen.FileDestination;
import com.googlecode.wmlynar.gwtbeans.gen.Mappings;
import com.googlecode.wmlynar.gwtbeans.xml.MappingsXmlReader;
import com.thoughtworks.qdox.JavaDocBuilder;

/**
 * A Mojo for generating BindingFactory. Based on Wilfred Sptinger's spring-me
 * maven plugin for spring-me framework (www.springframework.me).
 * 
 * @author Wojciech Mlynarczyk
 * @goal generate
 * @phase generate-sources
 * @requiresDependencyResolution compile
 */
public class GeneratorMojo extends AbstractMojo {

    /**
     * @parameter expression="${project}"
     * @required
     */
    private MavenProject project;

    /**
     * @parameter expression="${localRepository}"
     */
    private ArtifactRepository localRepository;

    /**
     * @component
     */
    protected ArtifactFactory artifactFactory;

    /**
     * @parameter
     * @required
     */
    private File configFile;

    /**
     * @parameter
     * @required
     */
    private String className;

    /**
     * @parameter
     * @required
     */
    private String packageName;

    /**
     * @parameter expression="${basedir}/target/generated-sources/gwt-beans"
     * @required
     */
    private File targetDirectory;

    public void execute() throws MojoExecutionException, MojoFailureException {

        final MappingsXmlReader reader = new MappingsXmlReader();
        Mappings mappings;
        try {
            mappings = reader.read(configFile.getAbsolutePath());
        } catch (final Exception e) {
            throw new MojoExecutionException("Could not parse xml configuration file: "
                    + e.toString());
        }

        final JavaDocBuilder builder = createJavaDocBuilder();
        final BindingFacoryGenerator generator = new BindingFacoryGenerator(builder);

        final String content = generator.generate(mappings, packageName, className);
        final FileDestination destination = new FileDestination();

        try {
            destination.write(className, packageName, targetDirectory.getAbsolutePath(), content);
        } catch (final IOException e) {
            throw new MojoExecutionException("Could not write to a file: " + e.toString());
        }

        project.addCompileSourceRoot(targetDirectory.getAbsolutePath());
    }

    /**
     * Copied for Wilfred Springer's spring-me framework
     * (www.springframework.me).
     */
    @SuppressWarnings("unchecked")
    private JavaDocBuilder createJavaDocBuilder() throws MojoExecutionException {
        final JavaDocBuilder builder = new JavaDocBuilder();
        builder.addSourceTree(new File(project.getBuild().getSourceDirectory()));
        // Quick and dirty hack for adding Classloaders with the definitions of
        // classes the sources depend upon.
        try {
            final Set<Artifact> artifacts = MavenMetadataSource.createArtifacts(artifactFactory,
                    project.getDependencies(), Artifact.SCOPE_RUNTIME, new ArtifactFilter() {

                        public boolean include(final Artifact artifact) {
                            return true;
                        }

                    }, project);
            final List<URL> urls = new ArrayList<URL>();
            for (final Artifact artifact : artifacts) {
                if (getLog().isDebugEnabled()) {
                    getLog().debug("Adding artifact " + artifact.getId());
                }
                if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) {
                    urls.add(artifact.getFile().toURL());
                } else {
                    urls.add(new File(localRepository.getBasedir(), localRepository
                            .pathOf(artifact)).toURL());
                }
            }
            final URL[] template = new URL[0];
            final URLClassLoader loader = new URLClassLoader(urls.toArray(template));
            builder.getClassLibrary().addClassLoader(loader);
        } catch (final InvalidDependencyVersionException idve) {
            idve.printStackTrace();
        } catch (final MalformedURLException e) {
            throw new MojoExecutionException("Malformed artifact URLs.");
        }
        return builder;
    }

}
